package com.example.warehouse_management.controller;

import com.example.warehouse_management.entity.CurrentUser;
import com.example.warehouse_management.entity.LoginUser;
import com.example.warehouse_management.entity.Result;
import com.example.warehouse_management.entity.User;
import com.example.warehouse_management.service.UserService;
import com.example.warehouse_management.utils.DigestUtil;
import com.example.warehouse_management.utils.TokenUtils;
import com.example.warehouse_management.utils.WarehouseConstants;
import com.google.code.kaptcha.Producer;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * 登录相关接口控制器
 * 负责处理与用户登录相关的请求，如验证码生成、用户登录验证等
 */
@RestController
public class LoginController {

    /**
     * 注入验证码生成器实例
     * 由Spring容器管理，具体实现类在配置类（如CaptchaConfig）中定义（通常为DefaultKaptcha）
     * name属性指定要注入的Bean名称，需与配置类中定义的Bean名称保持一致
     */
    @Resource(name = "captchaProducer")
    private Producer producer;


    /**
     * 注入Redis操作模板
     * 用于将生成的验证码文本暂存到Redis中并设置过期时间，便于后续登录时验证验证码的有效性
     */
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 生成验证码图片接口
     * 前端请求此接口获取验证码图片，同时后端将验证码文本存入Redis，设置30分钟过期时间
     * 后续用户登录时需提交验证码，后端从Redis中验证其正确性
     *
     * @param response HttpServletResponse对象，用于向客户端输出验证码图片
     */
    @RequestMapping("/captcha/captchaImage")
    public void captchaImage(HttpServletResponse response) {
        // 声明Servlet输出流，用于向客户端写入图片数据
        ServletOutputStream out = null;
        try {
            // 1. 使用验证码生成器生成随机文本（如：ABCD12）
            String text = producer.createText();

            // 2. 根据生成的文本创建对应的验证码图片（在内存中生成BufferedImage对象）
            BufferedImage image = producer.createImage(text);

            // 3. 将验证码文本存入Redis，设置过期时间为30分钟（60秒*30）
            //    key: 验证码文本，value: 空字符串（仅需验证存在性）
            //    过期时间确保验证码不会永久有效，提升安全性，防止验证码被长期滥用
            redisTemplate.opsForValue().set(text, "", 60 * 30, TimeUnit.SECONDS);

            // 4. 设置响应头信息，指定返回内容为JPEG图片，让前端能正确解析图片类型
            response.setContentType("image/jpeg");

            // 5. 获取响应输出流，用于写入图片数据，建立与前端的输出通道
            out = response.getOutputStream();

            // 6. 将内存中的验证码图片以JPEG格式写入输出流，发送给前端，完成图片数据的传输
            ImageIO.write(image, "jpg", out);

            // 7. 刷新输出流，确保所有图片数据都被发送到前端，避免数据残留
            out.flush();

        } catch (IOException e) {
            // 捕获IO异常（如输出流写入失败、图片生成过程中IO错误等），包装为运行时异常抛出，便于上层统一处理
            throw new RuntimeException(e);
        } finally {
            // 8. 确保输出流资源被关闭，避免内存泄漏，无论是否发生异常都执行关闭操作
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // 关闭流时的异常仅打印，不影响主流程，因为流资源已尽力去关闭
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 注入用户服务接口，用于处理用户相关的业务逻辑，如根据账号查询用户、用户信息管理等
     */
    @Autowired
    private UserService userService;

    /**
     * 注入Token工具类，用于生成JWT令牌，实现用户登录状态的保持和验证
     */
    @Autowired
    private TokenUtils tokenUtils;


    /**
     * 用户登录接口
     * 接收前端传递的登录信息，依次验证验证码、用户账号存在性、用户状态、密码等，验证通过后生成JWT令牌并返回
     *
     * @param loginUser 前端传递的登录用户信息，包含用户账号、密码、验证码等必要登录信息
     * @return 登录结果，包含成功或失败信息，成功时附带用于后续身份验证的JWT令牌
     */
    @RequestMapping("/login")
    public Result login(@RequestBody LoginUser loginUser) {
        // 1. 验证验证码：从登录信息中获取验证码，检查Redis中是否存在该验证码，不存在则直接返回验证码错误
        String code = loginUser.getVerificationCode();
        if (!redisTemplate.hasKey(code)) {
            return Result.err(Result.CODE_ERR_BUSINESS, "验证码错误");
        }

        // 2. 查询用户是否存在：根据登录信息中的用户账号查询用户，若查询不到则返回账号不存在
        User user = userService.queryUserByCode(loginUser.getUserCode());
        if (user == null) {
            return Result.err(Result.CODE_ERR_BUSINESS, "账号不存在");
        }

        // 3. 验证用户状态：检查用户状态是否为已审核（通过常量WarehouseConstants.USER_STATE_PASS标识），未审核则返回对应提示
        if (!user.getUserState().equals(WarehouseConstants.USER_STATE_PASS)) {
            return Result.err(Result.CODE_ERR_BUSINESS, "用户未审核");
        }

        // 4. 验证密码：先获取前端输入的密码，进行加密处理，再与数据库中存储的加密密码比对，不一致则返回密码错误
        String inputPwd = loginUser.getUserPwd();
        String encryptedInputPwd = DigestUtil.hmacSign(inputPwd);
        if (!encryptedInputPwd.equals(user.getUserPwd())) {
            return Result.err(Result.CODE_ERR_BUSINESS, "用户名或密码错误");
        }

        // 5. 生成Token并返回成功结果：若以上验证都通过，构建当前用户信息对象，生成JWT令牌，返回登录成功及令牌
        CurrentUser currentUser = new CurrentUser(user.getUserId(), user.getUserCode(), user.getUserName());
        String token = tokenUtils.loginSign(currentUser, encryptedInputPwd);
        return Result.ok("登录成功", token);
    }
}